package com.ph.custom.aspect;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.alibaba.fastjson.JSON;
import com.ph.custom.annotation.Token;
import com.ph.custom.cache.CacheContent;
import com.ph.custom.cenum.TokenCheckEnum;
import com.ph.custom.processor.UserCacheAnnotationProcessor;
import com.ph.custom.processor.UserCacheProcessor;
import com.ph.shopping.common.util.container.ContainerUtil;
import com.ph.shopping.common.util.http.IPUtil;
import com.ph.util.ResultUtil;
import com.ph.util.TokenCheckUtil;

/**
 * 
 * @ClassName:  TokenCheckAspect   
 * @Description:token操作
 * @author: lijie
 * @date:   2017年4月16日 下午9:42:37     
 * @Copyright: 2017
 */
@Aspect
@Component
public class TokenCheckAspect {

	private static final Logger LOG = LoggerFactory.getLogger(TokenCheckAspect.class);

	@Pointcut("execution(* com.ph.controller..*Controller.*(..)) "
			+ "&& @annotation(org.springframework.web.bind.annotation.RequestMapping) "
			+ "&& @annotation(com.ph.custom.annotation.Token)")
	public void tokenChecTrack() {}

	@Autowired
	private UserCacheAnnotationProcessor userCacheAnnotationProcessor;

	@Autowired
	private UserCacheProcessor userCacheProcessor;

	// 防止重复提交缓存
	private final ConcurrentMap<String, String> tokenMap = new ConcurrentHashMap<String, String>();

	@Around("tokenChecTrack()")
	public Object tokenCheck(ProceedingJoinPoint point) {
		Object result = null;
		String rToken = getTokenByRequest(point);
		boolean flag = StringUtils.isNotBlank(rToken);
		try {
			if (flag) {
				String th = tokenMap.get(rToken);
				if (th != null && th.equals(rToken)) {
					return ResultUtil.setResult(false, "不能重复提交");
				}
				tokenMap.put(rToken, rToken);
			}
			if (beforeHandler(point)) {
				toUpdateHandler(point);
				// 执行方法
				result = point.proceed();
			} else {
				LOG.warn("非法请求,非法请求 IP = {}", JSON.toJSONString(getRequestIp(point)));
				result = ResultUtil.setResult(false, getTokenErrorDesc(point));
			}
		} catch (Throwable e) {
			LOG.error("handler token error", e);
		} finally {
			// 释放当前请求的标记
			if (flag) {
				tokenMap.remove(rToken, rToken);
			}
		}
		return result;
	}

	/**
	 * 
	 * @Title: getTokenId   
	 * @Description: 根据请求得到token   
	 * @param: @param point
	 * @param: @return      
	 * @return: String      
	 * @throws
	 */
	private String getTokenByRequest(JoinPoint point) {

		return getTokenByName(getArg(HttpServletRequest.class, point));
	}
	/**
	 * 
	 * @Title: getRequestIp   
	 * @Description: 得到请求IP   
	 * @param: @param point
	 * @param: @return      
	 * @return: String      
	 * @throws
	 */
	private String getRequestIp(JoinPoint point) {
		String ip = "";
		HttpServletRequest request = getArg(HttpServletRequest.class, point);
		if (request != null) {
			ip = IPUtil.getIpAddress(request);
		}
		return ip;
	}
	/**
	 * 
	 * @Title: beforeHandler   
	 * @Description: token 校验操作
	 * @param: @param point
	 * @param: @return
	 * @param: @throws Throwable      
	 * @return: boolean      
	 * @throws
	 */
	private boolean beforeHandler(JoinPoint point) throws Throwable {
		boolean flag = true;
		TokenCheckEnum[] resouces = checkUserCache(point, new TokenCheckEnum[] { TokenCheckEnum.CHECK });
		if (ArrayUtils.isNotEmpty(resouces)) {
			// 请求对象
			HttpServletRequest request = getArg(HttpServletRequest.class, point);
			flag = handlerCheck(request, resouces);
		}
		return flag;
	}
	/**
	 * 
	 * @Title: getTokenErrorDesc   
	 * @Description:得到注解描述
	 * @param: @param point
	 * @param: @return      
	 * @return: String      
	 * @throws
	 */
	private String getTokenErrorDesc(JoinPoint point){
		String desc = "";
		Token token = getTokenAnnotation(point);
		if(token != null){
			desc = token.errorDesc();
		}
		return desc;
	}
	/**
	 * 
	 * @Title: afterHandler   
	 * @Description:更新
	 * @param: @param point
	 * @param: @throws Throwable      
	 * @return: void      
	 * @throws
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	private void toUpdateHandler(JoinPoint point) throws Throwable {
		TokenCheckEnum[] resouces = checkUserCache(point, new TokenCheckEnum[] { TokenCheckEnum.PUT });
		if (ArrayUtils.isNotEmpty(resouces)) {
			// 请求对象
			CacheContent content = getArg(CacheContent.class, point);
			if (content != null) {
				String token = TokenCheckUtil.getToken();
				handlerBackResult(resouces, token);
				content.setName(TokenCheckUtil.TOKEN_NAME);
				content.setContent(token);
			}
		}
	}
	
	/**
	 * 
	 * @Title: getArg   
	 * @Description: 得到方法参数  
	 * @param: @param clas
	 * @param: @param point
	 * @param: @return      
	 * @return: T      
	 * @throws
	 */
	@SuppressWarnings("unchecked")
	private <T> T getArg(Class<T> clas, JoinPoint point) {
		T t = null;
		Object[] objects = point.getArgs();
		if (ArrayUtils.isNotEmpty(objects)) {
			for (Object object : objects) {
				if (clas.isInstance(object)) {
					t = (T) object;
					break;
				}
			}
		}
		return t;
	}
	/**
	 * 
	 * @Title: handlerCheck   
	 * @Description: 处理token 校验   
	 * @param: @param request
	 * @param: @return
	 * @param: @throws Throwable      
	 * @return: boolean      
	 * @throws
	 */
	private boolean handlerCheck(HttpServletRequest request, TokenCheckEnum[] resouces) throws Throwable {
		boolean flag = false;
		String token = getTokenByName(request);
		if (StringUtils.isNotBlank(token)) {
			String checkResult = handlerBackResult(resouces, token);
			flag = token.equals(checkResult);
		}
		return flag;
	}
	/**
	 * 
	 * @Title: getTokenByName   
	 * @Description: 根据token name 获取token   
	 * @param: @param request
	 * @param: @return      
	 * @return: String      
	 * @throws
	 */
	private String getTokenByName(HttpServletRequest request) {
		if (request != null) {
			return request.getParameter(TokenCheckUtil.TOKEN_NAME);
		}
		return null;
	}
	/**
	 * 
	 * @Title: resultResouce   
	 * @Description:得到注解资源   
	 * @param: @param point
	 * @param: @return      
	 * @return: UserCacheEnum[]      
	 * @throws
	 */
	private TokenCheckEnum[] annotationResouce(JoinPoint point) {
		TokenCheckEnum[] result = null;
		Token token = getTokenAnnotation(point);
		if (token != null) {
			result = token.tokenCache();
		}
		return result;
	}
	/**
	 * 
	 * @Title: getTokenAnnotation   
	 * @Description: 得到注解对象   
	 * @param: @param point
	 * @param: @return      
	 * @return: Token      
	 * @throws
	 */
	private Token getTokenAnnotation(JoinPoint point){
		Token token = null;
		MethodSignature methodSignature = (MethodSignature) point.getSignature();
		if (methodSignature != null) {
			Method method = methodSignature.getMethod();
			if (method != null) {
				token = userCacheAnnotationProcessor.tokenCheckannotationProcessor(method);
			}
		}
		return token;
	}
	/**
	 * 
	 * @Title: checkUserCache   
	 * @Description: 校验枚举值   
	 * @param: @param point
	 * @param: @param targets
	 * @param: @return      
	 * @return: UserCacheEnum[]      
	 * @throws
	 */
	private TokenCheckEnum[] checkUserCache(JoinPoint point, TokenCheckEnum[] targets) {
		TokenCheckEnum[] result = null;
		TokenCheckEnum[] userCaches = annotationResouce(point);
		if (ArrayUtils.isNotEmpty(userCaches)) {
			List<TokenCheckEnum> enums = ContainerUtil.aList();
			for (TokenCheckEnum uenum : userCaches) {
				if (uenum == null)
					continue;
				if (isTargetExists(targets, uenum)) {
					enums.add(uenum);
				}
			}
			if (!enums.isEmpty()) {
				result = enums.toArray(new TokenCheckEnum[enums.size()]);
			}
		}
		return result;
	}
	/**
	 * 
	 * @Title: isTargetExists   
	 * @Description: 查找目标对象   
	 * @param: @param targets
	 * @param: @param target
	 * @param: @return      
	 * @return: boolean      
	 * @throws
	 */
	private boolean isTargetExists(TokenCheckEnum[] targets, TokenCheckEnum target) {
		Arrays.sort(targets);
		return Arrays.binarySearch(targets, target) >= 0;
	}
	/**
	 * 
	 * @Title: handlerBackResult   
	 * @Description:处理token   
	 * @param: @param resoure
	 * @param: @param token
	 * @param: @return
	 * @param: @throws Throwable      
	 * @return: String      
	 * @throws
	 */
	private String handlerBackResult(TokenCheckEnum[] resoure, String token) throws Throwable {
		String result = null;
		if (ArrayUtils.isNotEmpty(resoure)) {
			Arrays.sort(resoure);
			for (int i = 0; i < resoure.length; i++) {
				TokenCheckEnum uenum = resoure[i];
				if (uenum == null)
					continue;
				result = userCacheProcessor.tokenPreHandle(uenum, token);
			}
		}
		return result;
	}
}
